package com.xquant.trade.utils;

import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class POIWordUtil {
    private static Logger logger = LoggerFactory.getLogger(POIWordUtil.class);

    /**
     * 传入一个二维数组
     *
     * @param rows
     * @return
     */
    public static String generateTable(ArrayList<ArrayList<String>> rows) {
        String tableInner = "";
        int num = 1;
        for (ArrayList<String> row : rows) {

            tableInner += num++ + "#";
            for (String s : row) {
                if (Pattern.matches("\\d{4}-\\d{2}-\\d{2}", s)) {
                    s = DateUtils.formatChineseDate(s);
                }
                tableInner = tableInner + s + "#";
            }
            tableInner = tableInner + "&";
        }
        return tableInner;
    }

    /**
     * 读入doc
     *
     * @param path
     * @return
     * @throws Exception
     */
    public static String readDoc(String path) {
        String buffer = "";
        try {
            if (path.endsWith(".doc")) {
//                InputStream is = new FileInputStream(path);
//                WordExtractor ex = new WordExtractor(is);
//                buffer = ex.getText();
//                ex.close();
            } else if (path.endsWith(".docx")) {
                InputStream is = new FileInputStream(path);
                XWPFDocument doc = new XWPFDocument(is);
                XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
                buffer = extractor.getText();
                extractor.close();
            } else {
                throw new RuntimeException("[" + path + "]不是doc/docx文件");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return buffer;
    }


    /**
     * 写出docx
     *
     * @param path
     */
    public static void writeDoc(String path) {
        // 创建一个文档
        XWPFDocument doc = new XWPFDocument();
        // 创建一个段落
        XWPFParagraph para = doc.createParagraph();
        // 一个XWPFRun代表具有相同属性的一个区域
        XWPFRun run = para.createRun();
        run.setBold(true); // 加粗
        run.setText("加粗的内容");
        run = para.createRun();
        run.setColor("FF0000");
        run.setText("红色的文字。");
        try {
            OutputStream os = new FileOutputStream(path);
            doc.write(os);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static File replace(String srcPath, String desPath, Map<String, String> params) throws IOException //throws IOException
    {
        // XWPFDocument doc = read(srcPath);
        XWPFDocument doc = new XWPFDocument(POIXMLDocument.openPackage(srcPath));
        // 替换段落里面的变量
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        XWPFParagraph paragraph;
        String badWords = new String();
        String badMatches = new String();
        while (iterator.hasNext()) {
            paragraph = iterator.next();
            Map<String, String> resTmp = replaceInPara(paragraph, params, null, null);
            if (!resTmp.get("badWords").isEmpty())
                badWords += resTmp.get("badWords");
            if (!resTmp.get("badMatches").isEmpty())
                badMatches += resTmp.get("badMatches");
        }
        // 替换表格里面的变量
        Map<String, String> resTmp = replaceInTable(doc, params);
        if (!resTmp.get("badWords").isEmpty())
            badWords += resTmp.get("badWords");
        if (!resTmp.get("badMatches").isEmpty())
            badMatches += resTmp.get("badMatches");
        write(desPath, doc);

        doc = read(srcPath);
        XWPFDocument doc2 = read(desPath);

        // 设置替换文档后的格式
        setStyle(doc, doc2, params);

        write(desPath, doc2);

        Map<String, String> res = new HashMap<>();
        res.put("badWords", badWords);
        res.put("badMatches", badMatches);
        return new File(desPath);
    }

    private static XWPFDocument read(String src) {
        InputStream is = null;
        XWPFDocument doc = null;
        try {
            is = new FileInputStream(src);
            doc = new XWPFDocument(is);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        close(is);
        return doc;
    }

    private static void write(String des, XWPFDocument doc) {
        OutputStream os = null;
        try {
            os = new FileOutputStream(des);
            doc.write(os);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        close(os);
    }
/*

    public static File ConvertPDF(File sourceFile,File target)  {
        OutputStream os = null;
        try {
            os = new FileOutputStream(target);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        WordprocessingMLPackage wordMLPackage = null;
        try {
            wordMLPackage = WordprocessingMLPackage.load(sourceFile);
            try {
                os = new FileOutputStream(sourceFile);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            org.docx4j.convert.out.pdf.PdfConversion c = new org.docx4j.convert.out.pdf.viaXSLFO.Conversion(wordMLPackage);
            //清除pdf页码额外调试信息
            Docx4jProperties.getProperties().setProperty("docx4j.Log4j.Configurator.disabled", "true");
//        org.docx4j.convert.out.pdf.viaXSLFO.Conversion.log.setLevel(Level.OFF);
            c.output(os, new PdfSettings());
        } catch (Docx4JException e) {
            e.printStackTrace();
        }
        return target;

    }
*/

    private static void close(Closeable closeable) {
        try {
            if (closeable != null)
                closeable.close();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

    /**
     * 替换XWPFParagraph里面的变量
     * 可能bug：world拆分run的时候可能把变量拆开，导致替换失败。
     * 解决方法：world模板里的变量名从纯文本中复制。具体方案待解决。
     *
     * @param para   段落
     * @param params 参数
     */
    private static Map<String, String> replaceInPara(XWPFParagraph para, Map<String, String> params, XWPFTableRow row, XWPFTable table) {
        List<XWPFRun> runs;
        Matcher matcher;
        String badWords = new String();
        String badMatches = new String();
        String badWordsTmp = new String();
        boolean badFlag = false;
        if (matcher(para.getParagraphText()).find()) {
            runs = para.getRuns();
            logger.info("size={},{}", runs.size(), runs.toString());
            for (int i = 0; i < runs.size(); i++) {
                XWPFRun run = runs.get(i);
                int fontSize = run.getFontSize();
                String fontFamily = run.getFontFamily();
                String runText = run.toString();
                matcher = matcher(runText);
                if (badFlag) {
                    if (runText.contains(",")) {
                        badFlag = false;
                    } else {
                        if (runText.contains("}")) {
                            badWords += badWordsTmp + runText + ",";
                            //badWords += runText + ",";
                            badFlag = false;
                        } else
                            badWordsTmp += runText;
                        //badWords += runText;
                    }
                }
                if (runText.startsWith("{") && (!runText.contains("}")) && (!runText.contains(","))) {
                    badWordsTmp = runText;
                    //badWords += runText;
                    badFlag = true;
                }

                if (matcher.find()) {
                    while ((matcher = matcher(runText)).find()) {
                        String tmpStr = runText;
                        runText = matcher.replaceFirst(String.valueOf(params.get(matcher.group(1))));
                        if (runText.equals("null") || runText.isEmpty())
                            badMatches += tmpStr + ",";
                    }
                    // 直接调用XWPFRun的setText()方法设置文本时，在底层会重新创建一个XWPFRun，把文本附加在当前文本后面，
                    // 所以不能直接设置，需要先删除当前run，然后再自己手动插入一个新的Run。
                    para.removeRun(i);
                    if (runText.contains("@ "))//word自动换行
                    {
                        String[] textStrings = runText.split("@");
                        for (int j = 0; j < textStrings.length - 1; j++) {

                            //System.out.println(">>>>>textStrings>>"+textStrings[i]);

                            XWPFRun runReplace = para.createRun();
                            CTFonts font = runReplace.getCTR().addNewRPr().addNewRFonts();
                            font.setEastAsia("楷体_GB2312");
                            runReplace.setFontFamily(fontFamily);
                            runReplace.setFontSize(fontSize);
                            runReplace.setText(textStrings[j]);
                            runReplace.addBreak();
                            //para.insertNewRun(i).setText(textStrings[j]);
                            //para.insertNewRun(i).setFontFamily(fontFamily);
                            //para.insertNewRun(i).setFontSize(fontSize);
                            //insertNewRun.addCarriageReturn();
                            //para.insertNewRun(i).addBreak();//换行
                        }
                        XWPFRun runReplace = para.createRun();
                        CTFonts font = runReplace.getCTR().addNewRPr().addNewRFonts();
                        font.setEastAsia("楷体_GB2312");
                        runReplace.setFontFamily(fontFamily);
                        runReplace.setFontSize(fontSize);
                        runReplace.setText(textStrings[textStrings.length - 1]);
                        //runReplace.addBreak();
                        //para.insertNewRun(i).setText(textStrings[0]);
                    }
                    /*
                     * 表格形式：须有完整第一行，后续行根据传入变量自动添加，目前仅用于结算通知书
                     * --------------------------
                     * |	abc	|	def	|	hij	|
                     * |		|		|		|
                     * --------------------------
                     * |	klm	|	nop	|	qrs	|
                     * |		|		|		|
                     * --------------------------
                     *
                     * 传入变量abc#def#hij&klm#nop#qrs，替换值需放在表格左上角
                     *
                     *
                     */
                    else if (runText.contains("&")) {//excel换行
                        String[] lines = runText.split("&");
                        for (int j = 0; j < lines.length; j++) {
                            String[] cellTexts = lines[j].split("#");
                            for (int k = 0; k < cellTexts.length; k++) {
                                row.setHeight(20 * 20);
                                XWPFTableCell cell = row.getCell(k);
                                cell.setText(cellTexts[k]);

                                CTTc cttc = cell.getCTTc();
                                CTP ctp = cttc.getPList().get(0);
                                CTPPr ctppr = ctp.getPPr();
                                if (ctppr == null) {
                                    ctppr = ctp.addNewPPr();
                                }
                                CTJc ctjc = ctppr.getJc();
                                if (ctjc == null) {
                                    ctjc = ctppr.addNewJc();
                                }
                                if (k <= 2)
                                    ctjc.setVal(STJc.CENTER); //水平居中
                                else
                                    ctjc.setVal(STJc.RIGHT);
                            /*
                    			List<XWPFParagraph>paras = cell.getParagraphs();
                    			XWPFRun runReplace = paras.get(0).createRun();
    							CTFonts font = runReplace.getCTR().addNewRPr().addNewRFonts();
    							font.setEastAsia("仿宋");
    							runReplace.setFontFamily(fontFamily);
    							runReplace.setFontSize(fontSize);
    							runReplace.setText(cellTexts[k]);
    							*/

                            }
                            if (j < lines.length - 1) {
                                row = table.createRow();
                            }
                        }
                    } else
                        para.insertNewRun(i).setText(runText);
                }
            }
        }
        Map<String, String> res = new HashMap<>();
        res.put("badWords", badWords);
        res.put("badMatches", badMatches);
        return res;
    }

    /**
     * 替换表格里面的表格的变量
     *
     * @param table  要替换的表格
     * @param params 参数
     */
    private static Map<String, String> replaceInnerTable(XWPFTable table, Map<String, String> params) {
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        List<XWPFParagraph> paras;
        String badWords = new String();
        String badMatches = new String();
        rows = table.getRows();
        for (int i = 0; i < rows.size(); i++) {
            cells = rows.get(i).getTableCells();
            for (XWPFTableCell cell : cells) {
                paras = cell.getParagraphs();
                for (XWPFParagraph para : paras) {
                    Map<String, String> resTmp = replaceInPara(para, params, rows.get(i), table);
                    if (!resTmp.get("badWords").isEmpty())
                        badWords += resTmp.get("badWords");
                    if (!resTmp.get("badMatches").isEmpty())
                        badMatches += resTmp.get("badMatches");
                }
            }
        }
        Map<String, String> res = new HashMap<>();
        res.put("badWords", badWords);
        res.put("badMatches", badMatches);
        return res;
    }


    /**
     * 替换表格里面的变量
     *
     * @param doc    要替换的文档
     * @param params 参数
     */
    private static Map<String, String> replaceInTable(XWPFDocument doc, Map<String, String> params) {
        Iterator<XWPFTable> iterator = doc.getTablesIterator();
        XWPFTable table;
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        List<XWPFParagraph> paras;
        String badWords = new String();
        String badMatches = new String();
        while (iterator.hasNext()) {
            table = iterator.next();
            rows = table.getRows();
            for (XWPFTableRow row : rows) {
                cells = row.getTableCells();
                for (XWPFTableCell cell : cells) {
                    paras = cell.getParagraphs();
                    List<XWPFTable> innerTables = cell.getTables();
                    for (XWPFParagraph para : paras) {
                        Map<String, String> resTmp = replaceInPara(para, params, row, table);
                        if (!resTmp.get("badWords").isEmpty())
                            badWords += resTmp.get("badWords");
                        if (!resTmp.get("badMatches").isEmpty())
                            badMatches += resTmp.get("badMatches");
                    }
                    for (XWPFTable innerTable : innerTables) {
                        Map<String, String> resTmp = replaceInnerTable(innerTable, params);
                        if (!resTmp.get("badWords").isEmpty())
                            badWords += resTmp.get("badWords");
                        if (!resTmp.get("badMatches").isEmpty())
                            badMatches += resTmp.get("badMatches");
                    }

                }
            }
        }
        Map<String, String> res = new HashMap<>();
        res.put("badWords", badWords);
        res.put("badMatches", badMatches);
        return res;
    }


    /**
     * 正则匹配字符串
     *
     * @param str
     * @return
     */
    private static Matcher matcher(String str) {
        Pattern pattern = Pattern.compile("\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }


    /**
     * 将原来文件作为模板替换新文件替换文本部分的格式
     *
     * @param doc     替换后的文件
     * @param tempdoc 原文件
     * @param params
     */
    private static void setStyle(XWPFDocument tempdoc, XWPFDocument doc, Map<String, String> params) {
        Iterator<XWPFParagraph> paragraphsIterator = tempdoc.getParagraphsIterator();
        Iterator<XWPFParagraph> paragraphsIterator2 = doc.getParagraphsIterator();
        XWPFParagraph para;
        XWPFParagraph para2;
        while (paragraphsIterator.hasNext()) {
            para = paragraphsIterator.next();
            para2 = paragraphsIterator2.next();
            setStyleInPara(para, para2);
        }

        Iterator<XWPFTable> tablesIterator = tempdoc.getTablesIterator();
        Iterator<XWPFTable> tablesIterator2 = doc.getTablesIterator();
        XWPFTable table;
        XWPFTable table2;
        List<XWPFTableRow> rows;
        List<XWPFTableRow> rows2;
        List<XWPFTableCell> cells;
        List<XWPFTableCell> cells2;
        List<XWPFParagraph> paras;
        List<XWPFParagraph> paras2;
        while (tablesIterator.hasNext()) {
            table = tablesIterator.next();
            table2 = tablesIterator2.next();
            rows = table.getRows();
            rows2 = table2.getRows();
            for (int i = 0; i < rows.size(); i++) {
                cells = rows.get(i).getTableCells();
                cells2 = rows2.get(i).getTableCells();
                for (int j = 0; j < cells.size(); j++) {
                    paras = cells.get(j).getParagraphs();
                    paras2 = cells2.get(j).getParagraphs();
                    for (int k = 0; k < paras.size(); k++) {
                        setStyleInPara(paras.get(k), paras2.get(k));
                    }
                }
            }
        }
    }

    private static void setStyleInPara(XWPFParagraph para, XWPFParagraph para2) {
        List<XWPFRun> runs;
        List<XWPFRun> runs2;
        Matcher matcher;
        if (matcher(para.getParagraphText()).find()) {
            runs = para.getRuns();
            runs2 = para2.getRuns();
            for (int i = 0; i < runs.size(); i++) {
                XWPFRun run = runs.get(i);
                XWPFRun run2 = runs2.get(i);
                String runText = run.toString();
                matcher = matcher(runText);
                if (matcher.find()) {
                    //按模板文件格式设置格式
                    run2.getCTR().setRPr(run.getCTR().getRPr());
                }
            }
        }
    }

}
